1 module lib.meson;
2 
3 import std.file : exists, getcwd, mkdirRecurse;
4 import std.path : buildPath, absolutePath;
5 import std..string : indexOf;
6 import std.regex : ctRegex, matchFirst;
7 import std.stdio : writeln;
8 
9 import lib.process : executeAt;
10 import lib.set_compiler;
11 
12 /** The options used for CMake */
13 struct MesonOptions
14 {
15   /** The build config. Defaults to `"release"` */
16   string buildConfig = "release";
17 
18   /** The backend to use  */
19   string backend = "ninja";
20 
21   /** The compiler to use. If `CC` and `CXX` environment variables are not specified, it defaults to `clang` */
22   auto compiler = "clang";
23 }
24 
25 /**
26   Configure and build a Meson project at the given paths.
27 
28   Params:
29       rootDir = The root path which includes meson.build
30       buildDir = The building directory path
31       options = The options used for Meson
32 
33 */
34 void meson(string rootDir = getcwd(), string buildDir = buildPath(getcwd(),
35     "./build"), MesonOptions options = MesonOptions())
36 {
37   // compiler
38   const env = set_compiler(options.compiler);
39 
40   // build config
41   const buildConfig = convert_build_config(options.buildConfig);
42 
43   // sanitizer
44   const sanitizerArgs = convert_sanitizer(options.buildConfig);
45 
46   if (!buildDir.exists()) // guess if the folder exists, then it is already configured
47   {
48   configure: // if compile fails, the script will configure and try again
49     // make build dir
50     mkdirRecurse(buildDir);
51 
52     // configure
53     executeAt([
54         "meson", "setup", buildDir, rootDir, "--buildtype", buildConfig,
55         "--backend", options.backend
56         ] ~ sanitizerArgs, rootDir, env);
57   }
58   // build
59   try
60   {
61     executeAt(["meson", "compile", "-C", buildDir], rootDir, env);
62   }
63   catch (Exception ex)
64   {
65     // configure and try again
66     goto configure;
67   }
68 }
69 
70 /** Convert D config to Cmake config */
71 private auto convert_build_config(string buildConfig)
72 {
73   // build config
74   if (buildConfig.indexOf("debug") != -1)
75   {
76     return "debug";
77   }
78   else if (buildConfig.indexOf("release") != -1)
79   {
80     return "release";
81   }
82 
83   return "debug";
84 }
85 
86 private string[] convert_sanitizer(string buildConfig)
87 {
88   // extract the name of the sanitizer from the build config
89   auto sanitizeMatch = matchFirst(buildConfig, ctRegex!(r"sanitize-(\w*)"));
90   if (!sanitizeMatch.empty() && sanitizeMatch.length == 2)
91   {
92     return ["-Db_sanitize=" ~ sanitizeMatch[1], "-Db_lundef=false"];
93   }
94   return [];
95 }